﻿using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.Marshalling;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using static System.Security.Cryptography.ECCurve;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Utilities.IO.Pem;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Digests;
using System.Text.RegularExpressions;
using System.Security.Cryptography.Xml;
using BigInteger=Org.BouncyCastle.Math.BigInteger;
using System.Security.Cryptography.X509Certificates;
using System.Reflection.Metadata;


namespace WpfApp1
{
    public class SMKeyPair
    {
        public string PubKey { get; set; }
        public string PriKey { get; set; }
        public AsymmetricCipherKeyPair AsymmetricCipherKeyPair { get; set; }
        public SMKeyPair(string pubKey, string priKey, AsymmetricCipherKeyPair asymmetricCipherKeyPair)
        {
            this.PubKey = pubKey;
            this.PriKey = priKey;
            this.AsymmetricCipherKeyPair = asymmetricCipherKeyPair;
        }
    }
    public class PasswordFinder : IPasswordFinder
    {
        private readonly string password;

        public PasswordFinder(string password)
        {
            this.password = password;
        }

        public char[] GetPassword()
        {
            return password.ToCharArray();
        }
    }
    public enum Mode
    {
        C1C2C3, C1C3C2
    }

    public class SM2Crypto
    {        byte[] pubkey;
        byte[] privkey;
        Mode mode;
        public SM2Crypto(byte[] pubkey, byte[] privkey, Mode mode)
        {
            this.pubkey = pubkey;
            this.privkey = privkey;
            this.mode = mode;
        }
        public SM2Crypto(string pubkey, string privkey, Mode mode = Mode.C1C2C3, bool isPkcs8 = false)
        {
            if (!isPkcs8)
            {
                if (pubkey != null) this.pubkey = Decode(pubkey);
                if (privkey != null) this.privkey = Decode(privkey);
            }
            else
            {
                if (pubkey != null) this.pubkey = ((ECPublicKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(pubkey))).Q.GetEncoded();
                if (privkey != null) this.privkey = ((ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privkey))).D.ToByteArray();
            }
            this.mode = mode;
        }

        ICipherParameters _privateKeyParameters;
        ICipherParameters PrivateKeyParameters
        {
            get
            {
                var r = _privateKeyParameters;
                if (r == null) r = _privateKeyParameters = new ECPrivateKeyParameters(new Org.BouncyCastle.Math.BigInteger(1, privkey), new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
                return r;
            }
        }
        ICipherParameters _publicKeyParameters;
        ICipherParameters PublicKeyParameters
        {
            get
            {
                var r = _publicKeyParameters;
                if (r == null)
                {
                    var x9ec = GMNamedCurves.GetByName("SM2P256V1");
                    r = _publicKeyParameters = new ECPublicKeyParameters(x9ec.Curve.DecodePoint(pubkey), new ECDomainParameters(x9ec));
                }
                return r;
            }
        }

        public static void GenerateKeyHex(out string pubkey, out string privkey)
        {
            GenerateKey(out var a, out var b);
            pubkey = Hex.ToHexString(a);
            privkey = Hex.ToHexString(b);
        }
        private static void GenerateKey(out byte[] pubkey, out byte[] privkey,bool compress = true)
        {
            var g = new ECKeyPairGenerator();
            g.Init(new ECKeyGenerationParameters(new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")), new SecureRandom()));
            var k = g.GenerateKeyPair();
            pubkey = ((ECPublicKeyParameters)k.Public).Q.GetEncoded(compress);
            privkey = ((ECPrivateKeyParameters)k.Private).D.ToByteArray();
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public string Decrypt(string privateKey,string str)
        {
            //var data = Encoding.UTF8.GetBytes(str);
            //if (mode == Mode.C1C3C2) data = C132ToC123(data);
            //var sm2 = new SM2Engine();
            //sm2.Init(false, this.PrivateKeyParameters);
            //var bs = sm2.ProcessBlock(data, 0, data.Length);
            //var result = Encoding.UTF8.GetString(bs);
            //return result;

            byte[] cipherDataByte = Hex.Decode(str);

            //获取一条SM2曲线参数
            X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
            //构造domain参数
            ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve,
                    sm2ECParameters.G, sm2ECParameters.N);

            BigInteger privateKeyD = new BigInteger(privateKey, 16);
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.Init(false, privateKeyParameters);

            String result = null;
            try
            {
                byte[] arrayOfBytes = sm2Engine.ProcessBlock(cipherDataByte, 0, cipherDataByte.Length);
                //return new String(arrayOfBytes, "utf-8");
                string s = System.Text.Encoding.UTF8.GetString(arrayOfBytes);
                return s;
            }
            catch (Exception e)
            {
                //log.error("SM2解密时出现异常:", e);
            }
            return result;
        }
        public string Encrypt(string publicKey, string str)
        {
            try
            {
                // 获取一条SM2曲线参数
                X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
                // 构造domain参数
                ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve,
                        sm2ECParameters.G,
                        sm2ECParameters.N);
                //提取公钥点
                var pukPoint = sm2ECParameters.Curve.DecodePoint(Hex.Decode(publicKey));
                // 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
                ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

                SM2Engine sm2Engine = new SM2Engine();
                sm2Engine.Init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

                byte[] arrayOfBytes = null;
                try
                {
                    byte[] inBytes = System.Text.Encoding.UTF8.GetBytes(str);
                    arrayOfBytes = sm2Engine.ProcessBlock(inBytes, 0, inBytes.Length);
                }
                catch (Exception e)
                {
                    //log.error("SM2加密时出现异常:", e);
                }
                return Hex.ToHexString(arrayOfBytes);
            }
            catch (Exception ex)
            {
                return "";
            }
        }
        private  byte[] ToByteArray(string s)
        {
            byte[] array = new byte[s.Length];
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = Convert.ToByte(s[i]);
            }

            return array;
        }
        public  bool Verify(String publicKey, String content, String sign)
        {
            //待签名内容
            byte[] message = Hex.Decode(content);
            byte[] signData = Hex.Decode(sign);

            // 获取一条SM2曲线参数
            X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
            // 构造domain参数
            ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve,
                    sm2ECParameters.G,
                    sm2ECParameters.N);
            //提取公钥点
            var pukPoint = sm2ECParameters.Curve.DecodePoint(Hex.Decode(publicKey));
            // 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
            //创建签名实例
            SM2Signer sm2Signer = new SM2Signer();
            ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, ToByteArray("1234567812345678"));
            sm2Signer.Init(false, parametersWithID);
            sm2Signer.BlockUpdate(message, 0, message.Length);
            //验证签名结果
            var verify = sm2Signer.VerifySignature(signData);
            return verify;
        }
        public string Sign(string str,string privateKey, byte[] id = null)
        {
            //待签名内容转为字节数组
            byte[] message = Hex.Decode(str);

            //获取一条SM2曲线参数
            X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
            //构造domain参数
            ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve,
                    sm2ECParameters.G, sm2ECParameters.N);

            BigInteger privateKeyD = new BigInteger(privateKey, 16);
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

            //创建签名实例
            SM2Signer sm2Signer = new SM2Signer();

            //初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
            try
            {
                sm2Signer.Init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.GetInstance("SHA1PRNG")), ToByteArray("1234567812345678")));
            }
            catch (Exception e)
            {
                //log.error("签名时出现异常:", e);
            }
            sm2Signer.BlockUpdate(message, 0, message.Length);
            //生成签名,签名分为两部分r和s,分别对应索引0和1的数组
            byte[] signBytes = sm2Signer.GenerateSignature();
            String sign = Hex.ToHexString(signBytes);
            return sign;
        }
        public bool VerifySign(string msg, string signature, byte[] id = null)
        {
            byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
            byte[] signatureBytes = Encoding.UTF8.GetBytes(signature);
            var sm2 = new SM2Signer(new SM3Digest());
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(PublicKeyParameters, id);
            else cp = PublicKeyParameters;
            sm2.Init(false, cp);
            sm2.BlockUpdate(msgBytes, 0, msg.Length);
            return sm2.VerifySignature(signatureBytes);
        }
        static byte[] C123ToC132(byte[] c1c2c3)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。
            int c3Len = 32; //new SM3Digest().getDigestSize();
            byte[] result = new byte[c1c2c3.Length];
            Array.Copy(c1c2c3, 0, result, 0, c1Len); //c1
            Array.Copy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
            Array.Copy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
            return result;
        }
        static byte[] C132ToC123(byte[] c1c3c2)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
            int c3Len = 32; //new SM3Digest().getDigestSize();
            byte[] result = new byte[c1c3c2.Length];
            Array.Copy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
            Array.Copy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
            Array.Copy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
            return result;
        }
        static byte[] Decode(string key)
        {
            return Regex.IsMatch(key, "^[0-9a-f]+$", RegexOptions.IgnoreCase) ? Hex.Decode(key) : Convert.FromBase64String(key);
        }
    }


}
